package com.mslinn.yumifier {
    import com.mslinn.events.ItemSelectEvent;
    import com.mslinn.events.TagSelectEvent;
    import com.mslinn.yumifier.model.AppModel;
    import com.mslinn.yumifier.model.Item;
    import com.mslinn.yumifier.model.Service;
    import com.mslinn.yumifier.model.Tag;
    
    import flash.events.Event;
    import flash.events.StatusEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.net.URLVariables;
    
    import mx.collections.ArrayCollection;
    import mx.core.FlexGlobals;
    import mx.core.windowClasses.StatusBar;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.http.mxml.HTTPService;
    import mx.utils.Base64Encoder;
    import mx.utils.URLUtil;


    [Bindable] public class Services {
        private var app:AppModel = AppModel.getInstance();
        private static var instance:Services = new Services();

        
        public var serviceAC:ArrayCollection = new ArrayCollection([
            new Service("Delicious", "https://api.del.icio.us"),
            new Service("Pinboard", "https://api.pinboard.in"),
            new Service("Other", "https://api.")
        ]);
        
        public function deleteItem(item:Item):void {
            var httpService:HTTPService = serviceXml("/v1/posts/delete");
            httpService.resultFormat = "e4x";
            var urlVariables:URLVariables = new URLVariables();
            urlVariables.url = item.href;
            httpService.request = urlVariables;
            httpService.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void {
                var index:int = app.items.getItemIndex(item);
                app.items.removeItemAt(index);
                FlexGlobals.topLevelApplication.setStatus("Item deleted");
                if (app.items.length<=index)
                    index--;
                var selectedItem:Item = index>=0 ? Item(app.items.getItemAt(index)) : null;
                var itemSelectEvent:ItemSelectEvent = new ItemSelectEvent(selectedItem);
                FlexGlobals.topLevelApplication.dispatchEvent(itemSelectEvent);
            });
            httpService.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void {
                FlexGlobals.topLevelApplication.setStatus(event.toString());
            });
            FlexGlobals.topLevelApplication.setStatus("Deleting item");
            httpService.send();
        }
        
        public function urlRequestItems(tagFilter:String="", dateFilter:Date=null):HTTPService {
            var service:HTTPService;
            if (app.userId.length==0) {
                service = serviceSetup();
                service.url = app.serviceUrlJson + app.userId; // todo don't use hardcode url
            } else {
                service = serviceXml("/v1/posts/all");
                service.resultFormat = "e4x";
                var urlVariables:URLVariables = new URLVariables();
                if (tagFilter && tagFilter.length>0)
                    urlVariables.tag = tagFilter;
                if (dateFilter)
                    urlVariables.time = dateFilter.toString(); // todo format the string
                service.request = urlVariables;
            }
            return service;
        }
        
        public function get urlRequestTags():HTTPService {
            var service:HTTPService = serviceSetup();
            if (app.userId.length==0) {
                var urlParts:Array = app.serviceUrlJson.split("/");
                urlParts.pop();
                var serviceUrl:String = urlParts.join("/");
                service.url = app.serviceUrlJson + "/tags.json"; // todo don't use hardcode url
            } else {
                //service.url = "http://feeds.delicious.com/v2/json/tags/" + app.userId; // works
                service.url = app.serviceUrl + "/v1/tags/get"; // fails on Delicious with Error #2032: Stream Error. 
            }
            service.resultFormat = "text";
            service.showBusyCursor = true;
            return service;
        }
        
        public function loadTags():void {
            FlexGlobals.topLevelApplication.dispatchEvent(new ItemSelectEvent(null));
            var service:HTTPService = urlRequestTags;
            service.addEventListener(ResultEvent.RESULT, onTagsResult);
            service.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void {
                FlexGlobals.topLevelApplication.setStatus("Tags did not load");
            });
            FlexGlobals.topLevelApplication.setStatus("Requesting tags");
            service.send();
        }
        
        public static function getInstance():Services { return instance; }
        
        /** curl https://user:passwd@api.del.icio.us/v1/posts/all?tag=blah&meta=yes
        * <dl>
  <dt>&amp;tag={TAG}+{TAG}+...+{TAG}</dt>
    <dd>(optional) Filter by this tag.</dd>
  <dt>&amp;dt={CCYY-MM-DDThh:mm:ssZ}</dt>
    <dd>(optional) Filter by this date, defaults to the most recent date on which bookmarks were saved.</dd>
  <dt>&amp;url={URL}</dt>
    <dd>(optional) Fetch a bookmark for this URL, regardless of date.  Note: Be sure to URL-encode the argument value.</dd>
  <dt>&amp;hashes={MD5}+{MD5}+...+{MD5}</dt>
    <dd>(optional) Fetch multiple bookmarks by one or more URL MD5s regardless of date, separated by URL-encoded spaces (ie. '+').</dd>
  <dt>&amp;meta=yes</dt>
    <dd>(optional) Include change detection signatures on each item in a 'meta' attribute.  Clients wishing to maintain a synchronized local store of bookmarks should retain the value of this attribute - its value will change when any significant field of the bookmark changes.</dd>
</dl> */
        public function selectItems():void {
            var service:HTTPService = urlRequestItems(app.selectedTag, app.selectedDate);
            service.addEventListener(ResultEvent.RESULT, onItemsResult);
            service.addEventListener(FaultEvent.FAULT, function(event:Event):void {
                trace("selectItems: " + event);
                FlexGlobals.topLevelApplication.setStatus("Items could not  be obtained");
            });
            FlexGlobals.topLevelApplication.setStatus("Requesting items for tag(s)");
            service.send();
        }
        
        private function onItemsResult(event:ResultEvent):void {
            app.items = Item.decode(XML(event.result));
            FlexGlobals.topLevelApplication.setStatus("");
            FlexGlobals.topLevelApplication.dispatchEvent(new TagSelectEvent(app.selectedTag));
        }
        
        private function onTagsResult(event:ResultEvent):void {
            app.tags = Tag.decode(XML(event.result));
            FlexGlobals.topLevelApplication.setStatus("");
        }
        
        public function saveItem(item:Item):void {
            var httpService:HTTPService = serviceXml("/v1/posts/add");
            httpService.resultFormat = "e4x";
            var urlVariables:URLVariables = new URLVariables();
            urlVariables.url = item.href;
            urlVariables.description = item.description;
            urlVariables.extended = item.extended;
            urlVariables.tags = item.tags;
            urlVariables.dt = item.date;
            urlVariables.replace = "yes";
            urlVariables.shared = item.shared==true ? "yes" : "no";
            httpService.request = urlVariables;
            addAuthentication(httpService);
            httpService.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void {
                FlexGlobals.topLevelApplication.setStatus("Item saved");
            });
            httpService.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void {
                FlexGlobals.topLevelApplication.setStatus(event.toString());
            });
            FlexGlobals.topLevelApplication.setStatus("Saving item");
            httpService.send();
        }
        
        public function get selectedService():Service {
            var selectedService:Service = Service(serviceAC.getItemAt(serviceAC.length-1));
            var i:int = 0;
            for each(var s:Service in serviceAC)
            if (app.serviceUrl==Service(serviceAC.getItemAt(i++)).href)
                selectedService = s;
            return selectedService;
        }

        public function set selectedService(value:Service):void {
            app.serviceUrl = value.href;
        }
        
        /** @see http://stackoverflow.com/questions/490806/http-basic-authentication-with-httpservice-objects-in-adobe-flex-air */
        private function addAuthentication(service:HTTPService):HTTPService {
            var encoder:Base64Encoder = new Base64Encoder();
            encoder.insertNewLines = false;
            encoder.encode(app.userId + ":" + app.password);
            
            service.headers = { Authorization:"Basic " + encoder.toString() };
            return service;
        }
        
        private function serviceSetup(timeoutSeconds:int=5):HTTPService {
            var service:HTTPService = new HTTPService();
            service.requestTimeout = timeoutSeconds;
            service.concurrency = "last";
            addAuthentication(service);
            return service;
        }
        
        private function serviceXml(suffix:String):HTTPService {
            var url:String = URLUtil.getProtocol(app.serviceUrl) + "://" + /* port + ":" + */ URLUtil.getServerName(app.serviceUrl);
            var service:HTTPService = serviceSetup();
            service.url = url + suffix;
            return service;
        }
    }
}